<?php

namespace App\Utils;


use App\Pool\Redis\RateLimitPool;

class RateLimiter
{

    // 最大次数
    protected int $limit = 5;

    // 到达错误次数后，限制的时长限制时长
    protected int $lockSecond = 86400;

    // 记录错误次数的key
    protected string $cntCacheKey;

    public function __construct($cntCacheKey, $limit=5, $lockSecond=86400)
    {
        $this->cntCacheKey = $cntCacheKey;
        $this->setLimit($limit);
        $this->setLockSecond($lockSecond);
    }

    protected function getRedis()
    {
        return \Hyperf\Support\make(RateLimitPool::class);
    }

    // 失败次数+1,可以用lua脚本优化
    public function incrCnt()
    {
        $redis = $this->getRedis();

//        $count = (int)$redis->get($this->cntCacheKey);

//        $incr = $redis->incr($this->cntCacheKey);
//        if ($incr == 1){
//            $redis->expire($this->cntCacheKey, $this->lockSecond);
//        }
//        $lua = <<<LUA
//local key,ttl=KEYS[1],KEYS[2]
//if redis.call('EXISTS',key)==0 then
//  redis.call('SETEX',key,ttl,1)
//  return 1
//else
//  return tonumber(redis.call('INCR',key))
//end
//LUA;
        // redis7+
        $lua = <<<LUA
local current
current = redis.call("incr",KEYS[1])
if tonumber(current) == 1 then
    redis.call("expire",KEYS[1],KEYS[2])
end
LUA;

        $redis->eval($lua,[$this->cntCacheKey, $this->lockSecond],2);
    }

    // 清空失败次数
    public function freeLock()
    {
        $this->getRedis()->del($this->cntCacheKey);
    }

    // 判断是否被锁定
    public function isLocked()
    {
        return $this->cnt() >= $this->limit;
    }

    public function cnt()
    {
        return intval($this->getRedis()->get($this->cntCacheKey));
    }

    // 设置最大限制次数
    public function setLimit(int $limit)
    {
        $this->limit = $limit;
    }

    // 设置限制时长
    public function setLockSecond(int $seconds)
    {
        $this->lockSecond = $seconds;
    }

    /**
     * @param bool $humanLang true 返回 x小时x分x秒 | false 返回 剩余秒数
     * @return int|string
     */
    public function restLockTime($humanLang=true)
    {
        $seconds = $this->getRedis()->ttl($this->cntCacheKey);
        if (!$seconds || $seconds < 1){
            $seconds = 0;
        }
        if ($humanLang){
            return Helper::countDown($seconds);
        }
        return $seconds;
    }
}